{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "cb354baf",
   "metadata": {},
   "source": [
    "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langchain-ai/langchain-academy/blob/main/module-1/router.ipynb) [![Open in LangChain Academy](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66e9eba12c7b7688aa3dbb5e_LCA-badge-green.svg)](https://academy.langchain.com/courses/take/intro-to-langgraph/lessons/58239412-lesson-5-router)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "ce6fff79-25b5-4884-8aaa-e3ebb7ddd549",
   "metadata": {},
   "source": [
    "# Router\n",
    "\n",
    "## Review\n",
    "\n",
    "We built a graph that uses `messages` as state and a chat model with bound tools.\n",
    "\n",
    "We saw that the graph can:\n",
    "\n",
    "* Return a tool call\n",
    "* Return a natural language response\n",
    "\n",
    "## Goals\n",
    "\n",
    "We can think of this as a router, where the chat model routes between a direct response or a tool call based upon the user input.\n",
    "\n",
    "This is a simple example of an agent, where the LLM is directing the control flow either by calling a tool or just responding directly. \n",
    "\n",
    "![Screenshot 2024-08-21 at 9.24.09 AM.png](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66dbac6543c3d4df239a4ed1_router1.png)\n",
    "\n",
    "Let's extend our graph to work with either output! \n",
    "\n",
    "For this, we can use two ideas:\n",
    "\n",
    "(1) Add a node that will call our tool.\n",
    "\n",
    "(2) Add a conditional edge that will look at the chat model output, and route to our tool calling node or simply end if no tool call is performed. \n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "ebb4fc6e-7c85-4fc8-a4a9-0c7a527c4e5b",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install --quiet -U langchain_openai langchain_core langgraph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "885e92d9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "OPENAI_API_KEY:  ········\n"
     ]
    }
   ],
   "source": [
    "import os, getpass\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "_set_env(\"OPENAI_API_KEY\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "e3ba4df4-3045-49b1-9299-ced1fce14d24",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "def multiply(a: int, b: int) -> int:\n",
    "    \"\"\"Multiply a and b.\n",
    "\n",
    "    Args:\n",
    "        a: first int\n",
    "        b: second int\n",
    "    \"\"\"\n",
    "    return a * b\n",
    "\n",
    "llm = ChatOpenAI(model=\"gpt-4o\")\n",
    "llm_with_tools = llm.bind_tools([multiply])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c77555a2",
   "metadata": {},
   "source": [
    " We use the [built-in `ToolNode`](https://langchain-ai.github.io/langgraph/reference/prebuilt/?h=tools+condition#toolnode) and simply pass a list of our tools to initialize it. \n",
    " \n",
    " We use the [built-in `tools_condition`](https://langchain-ai.github.io/langgraph/reference/prebuilt/?h=tools+condition#tools_condition) as our conditional edge."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "9a6fde4e-cceb-4426-b770-97ee4b41e9da",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "from langgraph.graph import MessagesState\n",
    "from langgraph.prebuilt import ToolNode\n",
    "from langgraph.prebuilt import tools_condition\n",
    "\n",
    "# Node\n",
    "def tool_calling_llm(state: MessagesState):\n",
    "    return {\"messages\": [llm_with_tools.invoke(state[\"messages\"])]}\n",
    "\n",
    "# Build graph\n",
    "builder = StateGraph(MessagesState)\n",
    "builder.add_node(\"tool_calling_llm\", tool_calling_llm)\n",
    "builder.add_node(\"tools\", ToolNode([multiply]))\n",
    "builder.add_edge(START, \"tool_calling_llm\")\n",
    "builder.add_conditional_edges(\n",
    "    \"tool_calling_llm\",\n",
    "    # If the latest message (result) from assistant is a tool call -> tools_condition routes to tools\n",
    "    # If the latest message (result) from assistant is a not a tool call -> tools_condition routes to END\n",
    "    tools_condition,\n",
    ")\n",
    "builder.add_edge(\"tools\", END)\n",
    "graph = builder.compile()\n",
    "\n",
    "# View\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "11b608c5-0c15-4fb7-aa24-80ce5774fb85",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001b[1m Human Message \u001b[0m=================================\n",
      "\n",
      "Hello, what is 2 multiplied by 2? .\n",
      "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
      "Tool Calls:\n",
      "  multiply (call_Y587XAkv7ZQbrM8skMIM43cF)\n",
      " Call ID: call_Y587XAkv7ZQbrM8skMIM43cF\n",
      "  Args:\n",
      "    a: 2\n",
      "    b: 2\n",
      "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
      "Name: multiply\n",
      "\n",
      "4\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import HumanMessage\n",
    "messages = [HumanMessage(content=\"Hello, what is 2 multiplied by 2? .\")]\n",
    "messages = graph.invoke({\"messages\": messages})\n",
    "for m in messages['messages']:\n",
    "    m.pretty_print()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "34708377-16b6-4474-9e23-71890c1fb36e",
   "metadata": {},
   "source": [
    "Now, we can see that the graph runs the tool!\n",
    "\n",
    "It responds with a `ToolMessage`. \n",
    "\n",
    "## LangGraph Studio\n",
    "\n",
    "--\n",
    "\n",
    "**⚠️ DISCLAIMER**\n",
    "\n",
    "*Running Studio currently requires a Mac. If you are not using a Mac, then skip this step.*\n",
    "\n",
    "--\n",
    "\n",
    "Load the `router` in Studio, which uses `module-1/studio/router.py` set in `module-1/studio/langgraph.json`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "43782c33-0f41-47f2-ae38-ddb2cd4ba6f8",
   "metadata": {},
   "outputs": [],
   "source": [
    "#FINISHED LESSON\n",
    "#Learned\n",
    "#Okay just realized I've been doing these out of order... oh well\n",
    "#The lessons are listed in alphabetical order, not chronological\n",
    "#also tool_calling _llm is basically assistant in the other modules\n",
    "#in this case, we don't allow the model to circle back to assistant, so instead of a nicely formatted message\n",
    "#we just get the ToolMessage() from tools as the output\n",
    "#tools_conditions is just like decide_mood!\n",
    "#when the llm has addtional kwargs with a tool_call, we route to the tools node\n",
    "#otherwise just route to end"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
